<?PHP
/**
 * 数据库类
 * @author linyh,sg
 */
class SqlDB {
    protected static $db;
    protected static $selfObject;
    protected function __construct($host=null,$port=null,$user=null,$pass=null,$dbname=null) {
		if(isset(self::$db)) return;
		
		global $config;
		$host = !empty($host)?$host:$config['mysql']["host"];
		$port = !empty($port)?$port:$config['mysql']["port"];
		$user = !empty($user)?$user:$config['mysql']["username"];
		$pass = !empty($pass)?$pass:$config['mysql']["password"];
		$dbname = !empty($dbname)?$dbname:$config['mysql']["dbname"];

		try {
			$DSN = "mysql:host={$host};port={$port};dbname={$dbname}";
			$handle = new PDO($DSN,$user,$pass);
			$handle -> setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
			$handle-> query("set names utf8");
            self::$db = $handle;
		} catch(PDOException $e) {
			echo "Connection failed:".$e->getMessage();
		}
	}
	
	// 获取数据库操作类的引用对象
	static public function init($host=null,$port=null,$user=null,$pass=null,$dbname=null){
		if(!isset(self::$selfObject)){
			self::$selfObject= new SqlDB($host,$port,$user,$pass,$dbname);
		}
		return self::$selfObject;
	}

    /**
     * 对id格式化为sql查询语句
     * @param array|int|string $ids
     * @param string $idName
     * @return string
     */
    public function getIdCondition($ids, $idName="id"){
        $idName=explodeDeal(".",$idName,function($s){
            $s=trim($s);
            return validateVar(trim($s))? "`$s`": '';
        });
        if($ids===''||is_null($ids)){
            $idCondition="$idName is null";
        }elseif(is_array($ids)&&count($ids)>1){
            sort($ids);
            $idCondition="{$idName} in (".explodeDeal(",",$ids,function($s){return (int)$s;}). ')';
        }elseif(is_string($ids)&& !is_numeric($ids)){
            $idCondition="{$idName} in (".explodeDeal(",",$ids,function($s){return (int)$s;}). ')';
        }else{
            // 可能为int或其他
            $idCondition="{$idName}=".(int)(is_array($ids)? reset($ids): $ids);
        }
        return $idCondition;
    }

    // Sql事务
    function beginTransaction(){
        return self::$db->beginTransaction();
    }
    function commit(){
        return self::$db->commit();
    }
    function rollBack(){
        return self::$db->rollBack();
    }
    // 传递一个函数，该函数按照Sql事务的方式执行，出现任何错误均进行回滚
    function transaction($f){
        try{
            $this->beginTransaction();
            $re=call_user_func($f);
            $this->commit();
            return isset($re)?$re:true;
        } catch(PDOException $e) {
            $this->rollBack();
            return false;
        }
    }

    /**
     * 对一个字符串加引号并过滤
     * @param string $s
     * @return string
     */
    function quote($s){
        return self::$db->quote($s);
    }
    // 整数化一个参数
    function int($s){
        return (int)$s;
    }
    // 实数化一个参数
    function double($s){
        return (double)$s;
    }
    // 构造搜索字符串
    function search($s){
        return '%'.strtr($s,array("%"=>'\%','_'=>'\_')).'%';
    }
	
	/**
	* 函数直接执行sql语句
	* @param string $sql 要执行的SQL语句
	* @return int 返回修改的行数
	*/
	public function sqlExec($sql){
        $param=func_get_args();
        array_shift($param);
        if(empty($param)){
            return self::$db->exec($sql);
        }else{
            $stmt=$this->innerQuery($sql,$param);
            return $stmt->rowCount();
        }
	}

    protected function innerQuery($sql,$param=null){
        if(empty($param)){
            return self::$db->query($sql);
        }
        // 用于填充SQL语句中? 或者 :data 的内容
        $associativeParam=reset($param);
        if(is_array($associativeParam)){
            $param=$associativeParam;
        }
        $stmt=self::$db->prepare($sql);
        $stmt->execute($param);
        return $stmt;
    }

	/**
	* 检查sql语句是否能查询到内容
	* @param string $sql 要执行的SQL语句
	* @return bool 是否查询到内容
	*/
	function getExist($sql){
        $param=func_get_args();
        array_shift($param);
        $stmt=$this->innerQuery($sql,$param);
		$ifGet = $stmt->fetch(PDO::FETCH_BOUND);
		return $ifGet;
	}

	/**
	* 使用sql语句获取数据
	* 这一句sql必须保证只查询到一行一列
	* @param string $sql 要执行的SQL语句
	* @return string 获取到的内容
	*/
	function getValue($sql){
        $param=func_get_args();
        array_shift($param);
        $stmt=$this->innerQuery($sql,$param);
        $array=$stmt->fetch(PDO::FETCH_NUM);
        return !empty($array)?reset($array):null;
	}

	/**
	* 函数使用sql语句获取一行数据
	* @param string $sql 要执行的SQL语句
	* @return array 一个1维数组
	*/
	function getOne($sql){
        $param=func_get_args();
        array_shift($param);
        $stmt=$this->innerQuery($sql,$param);
        $array=$stmt->fetch(PDO::FETCH_ASSOC);
        return !empty($array)?$array:null;
	}

    /**
     * 函数使用sql语句获取所有查询到的数据
     * @param string $sql 要执行的SQL语句
     * @return array 一个2维数组
     */
    function getAll($sql){
        $param=func_get_args();
        array_shift($param);
        $stmt=$this->innerQuery($sql,$param);
        if(empty($stmt)) return array();
        return $stmt->fetchall(PDO::FETCH_ASSOC);
    }

    /**
     * 函数使用sql语句获取所有查询到的数据以及总条数
     * @param string $sql 要执行的SQL语句，必须包括 SQL_CALC_FOUND_ROWS
     * @return array(array,total)
     */
    function getList($sql){
        $param=func_get_args();
        array_shift($param);
        $stmt=$this->innerQuery($sql,$param);
        if(empty($stmt)) return array(array(),0);
        $data=$stmt->fetchall(PDO::FETCH_ASSOC);
        $total=$this->getValue("SELECT FOUND_ROWS()");
        return array($data,$total);
    }

    /**
     * 函数使用sql语句获取所有查询到的数据
     * @param string $sql 要执行的SQL语句
     * @return SqlDBStatement|null
     */
    function query($sql){
        $stmt = self::$db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
        $stmt->execute();
        return new SqlDBStatement($stmt);
    }

    /**
     * 生成复杂的查询语句
     * TODO 如果一个字段里面有一个*，则直接查询所有
     * 如该字段:array('count(1)','student S'=>array('id','num'=>'studentNum'),'project P'=>array('*'));
     * count(1),`S`.`id`,`S`.`num` as `studentNum`,`P`.*
     *
     * 如该条件:array('type'=>1,'age'=>'> 5','age '=>'<=12  ','icon'=>'like %.jpg', 'text'=>'', 'S.area'=>null, array('or','S.name','=','sg'),'`class` is not null');
     * 1=1 and `type` = 1 and `age` > 5 and `age` <= 12 and `icon` like '%.jpg' or `S`.`name` = 'sg' and `class` is not null
     *
     * @param array $data 要查询的数据
     * @param array $condition 查询使用的条件
     * @param int $rows 返回行数
     * @param int $offset 从第几行开始返回
     * @param string $sortBy 根据什么排序
     * @param bool $sortAsc 排序是否为Asc
     * @param string $attach 查询附加信息
     * @param bool $blankRetain 是否开启空白查询模式，默认关闭
     * @return string 返回Sql语句
     */
    public function selectSql($data, $condition, $rows=null, $offset=0, $sortBy=null, $sortAsc=true, $attach='', $blankRetain=false){
        assert(is_array($data)/*,'$data参数必须是数组'*/);
        assert(!empty($data)/*,'$data参数不能为空'*/);
        assert(is_array($condition)/*,'$condition参数必须是数组'*/);
        assert(!empty($condition)/*,'$condition参数不能为空'*/);
        $fieldArray=array();
        $table=array();

        // 处理查询字符串
        foreach($data as $tableName=>$v){
            if(!is_array($v)){
                // 如果是直接一个参数
                $fieldArray[]=trim($v);
            }else{
                // 如果是表名=>字段名数组
                $tableNameArray=explode(" ",$tableName);
                foreach($tableNameArray as &$tableV) $tableV='`'.trim($tableV).'`';
                $table[]=implode(" ",$tableNameArray);
                $tableName=end($tableNameArray);
                foreach($v as $source=>$target){
                    if(is_string($source)){
                        $fieldArray[]="$tableName.`$source` as `$target`";
                    }elseif($target!=='*'){
                        $fieldArray[]="$tableName.`$target`";
                    }else{
                        $fieldArray[]="$tableName.*";
                    }
                }
            }
        }

        $conditionStr='1=1 ';
        foreach($condition as $field=>$judge){
            if(!is_string($field)){
                if(is_array($judge)) {
                    // 形如 array('or','S.name','=','sg')
                    list($link, $field, $op, $value)=$judge;
                }else{
                    // 形如 '`class` is not null'
                    $conditionStr.="and $judge ";
                    continue;
                }
            }else{
                $link="and";
                $judge=trim($judge);
                // 形如 age=>'<=12  '
                if(is_numeric($judge)){
                    $op='=';
                    $value=0+$judge;
                }elseif(substr($judge, 0,1)==">"|| substr($judge, 0,1)=="<"|| substr($judge, 0,1)=="="){
                    if(substr($judge, 1,1)=="="|| substr($judge, 0,2)=="<>"){
                        $op=substr($judge, 0,2);
                        $value=trim(substr($judge, 2));
                    }else{
                        $op=substr($judge, 0,1);
                        $value=trim(substr($judge, 1));
                    }
                }elseif(strtolower(substr($judge, 0,5))=="like "){
                    $op=substr($judge, 0,5);
                    $value=trim(substr($judge, 5));
                }else{
                    $op='=';
                    $value=$judge;
                }
            }
            // 给字段名加上反引号
            $field=explodeDeal(".",$field,function($s){
                $s=trim($s);
                return validateVar(trim($s))? "`$s`": '';
            });

            // 根据值的类型决定生成的sql类型
            if(is_numeric($value)){
                $value=(int)$value;
            }else{
                if(empty($value)&& !$blankRetain) continue;
                $value=$this->quote(trim($value));
            }

            $conditionStr.="$link $field $op $value ";
        }

        $fieldStr=implode(",",$fieldArray);
        $tableStr=implode(",",$table);
        $sql="select SQL_CALC_FOUND_ROWS $fieldStr from $tableStr where $conditionStr";
        if(!empty($sortBy)){
            $sortBy=explodeDeal(",",$sortBy,function($s){
                return explodeDeal(".",trim($s),function($s){
                    return validateVar(trim($s))? ("`".trim($s)."`"): 0;
                });
            });
            $sql.=" order by $sortBy ". ($sortAsc?"Asc":"Desc");
        }
        $sql.= " ".$attach;
        if(!is_null($rows)){
            $sql.=" limit ".(int)$offset.",".(int)$rows;
        }
        return $sql;
    }

    // 函数使用sql语句获取所有查询到的数据
    public function select($data, $condition, $rows=null, $offset=0, $sortBy=null, $sortAsc=true, $attach='', $blankRetain=false){
        return $this->query($this->selectSql($data, $condition, $rows, $offset, $sortBy, $sortAsc, $attach, $blankRetain));
    }

	/**
     * 插入数据
     * @param string $tableName 要插入的表名
     * @param array $data 要插入的数组，键名为数据库字段名，值为要插入的值
     * @param bool $blankRetain 是否开启空白保留模式
     * @return int 插入了多少内容
     */
	function insert($tableName, $data, $blankRetain=false){
		$db=self::$db;

		if(!is_array($data) || empty($data)){
			return false; //没有要插入的数据
		}
        $line_arr=array();	//插入的列
        $value_arr=array();	//插入的值
        foreach($data as $key=>$result){
            $result=trim($result);
            if(!empty($result)|| is_numeric($result)|| $blankRetain){
                $line_arr[]="`{$key}`";
                $value_arr[]=$db->quote($result);
            }
        }
        if(empty($line_arr)) return 0;
        $line=implode(",",$line_arr);
        $value=implode(",",$value_arr);
        $sql="INSERT INTO `{$tableName}` ({$line})values({$value});";
        return $this->sqlExec($sql);
	}
	function insertId(){
		return self::$db->lastInsertId();
	}

	/**
	 * 更新数据
	 * @param string $tableName 表名
 	 * @param array|int $id 要修改的id号，如果id不存在则修改失败
	 * @param array $data 要更新的数据，键名为数据库字段名，值为要插入的值
     * @param bool $blankRetain 是否开启空白保留模式
	 * @return int 修改了多少内容
	 */
	function update($tableName, $id, $data, $blankRetain=false){
		$db=self::$db;
		
		//查询要修改的id号，如果id不存在则修改失败
		$up_arr=array();	//更新的语句
		if(!is_array($data)|| count($data)<=0|| empty($id)){
			return 0; //没有要插入的数据
		}
        foreach($data as $key=>$value){
            if(!empty($value)|| is_numeric($value)|| $blankRetain){
                $up_arr[]="`{$key}`=".$db->quote($value);
            }
        }
        if(empty($up_arr)) return 0;
        $up_str=implode(",",$up_arr);
        $sql="UPDATE `{$tableName}` set {$up_str} where ".$this->getIdCondition($id);
        return $this->sqlExec($sql);
	}

	/**
	 * 添加或者更新数据，根据id存在与否新建或者修改一条数据
	 * @param string $tableName 表名
	 * @param array|int $id 要修改的id号，如果id不存在则新建一行数据
	 * @param array $data 要更新的数据，键名为数据库字段名，值为要插入的值
     * @param bool $blankRetain 是否开启空白保留模式
	 * @return int 修改了多少内容
	 */
	function modify($tableName, $id, $data, $blankRetain=false){
        if(!empty($id)){
            $updateModifyNum=$this->update($tableName,$id,$data, $blankRetain);
        }else{
            $updateModifyNum=$this->insert($tableName,$data, $blankRetain);
        }
        return $updateModifyNum;
	}

	/**
     * 删除数据
     * @param string $tableName 表名
     * @param array|int $id
     * @return int 删除了多少内容
     */
	function delete($tableName, $id){
        if(empty($id)){
            return 0;
        }
        $sql="DELETE from `{$tableName}` where ".$this->getIdCondition($id);
        return $this->sqlExec($sql);
	}
}
/**
 * 用于构造复杂SQL查询语句的类
 * TODO 如果一个字段里面有一个*，则直接查询所有，并且别名都不会生效
 * TODO 有*的表会放在第一个，如果有字段名(如ID)和其他表重复，会被覆盖
 */
class SqlDBSelectSql {
    /** @var  SqlDB */
    public $db;
    protected $tableField;
    protected $fieldSql;
    protected $table;
    protected $tableSql;
    protected $conditionSql;
    protected $sortSql;

    function __construct() {
        $this->db=SqlDB::init();
        $this->conditionSql=array();
        $this->fieldSql=array();
        $this->tableSql=array();
        $this->sortSql=array();
    }
    // 是否需要SQL_CALC_FOUND_ROWS
    protected $ifFoundRows=true;
    public function setFoundRows($ifFoundRows=true) {
        $this->ifFoundRows = (bool)$ifFoundRows;
    }
    // 是否采取空白保留模式
    protected $ifBlankRemain=false;
    public function setBlankRemain($ifBlankRemain=true) {
        $this->ifBlankRemain = (bool)$ifBlankRemain;
    }

    // 划分表名
    public static function divideTableName($s, $needle=" "){
        $p=strpos($s,$needle);
        return $p? array(substr($s,0,$p),substr($s,$p+1)): array($s,$s);
    }
    // 格式化字段名，获取其中的表名、表名.字段名
    public function formatFieldName($s, $needle="."){
        $p=strpos($s,$needle);
        if($p) return array(substr($s,0,$p),$s);
        foreach($this->tableField as $tableName=>$fieldArray){
            $key=array_search($s,$fieldArray);
            if($key!==false){
                return array($tableName,is_numeric($key)? $s: $key);
            }
        }
        return array(null,$s);
    }
    protected function getFieldName($fieldName,$neddle="."){
        list($tableName,$fieldName)=$this->formatFieldName($fieldName,$neddle);
        if(isset($this->table[$tableName])){
            $tableName=$this->table[$tableName];
        }
        return array($tableName,$fieldName);
    }
    public function getFieldFullName($fieldName,$neddle="."){
        list($tableName,$fieldName)=$this->getFieldName($fieldName,$neddle);
        return isset($tableName)? "`$tableName`.`$fieldName`": "`$fieldName`";
    }

    // 增加表和要查询的字段，$field可以包含多个要查询的字段
    // 如果最后还包含 * 则查询所有(此时该表的as不会生效)
    public function addTable($tableName, $field=array()){
        if(empty($tableName)){
            // 如果表名为空
            $this->fieldSql[]=trim($field);
            return $this;
        }
        // 对表名进行划分并判断
        list($tableName,$tableNameAlias)=$this->divideTableName($tableName);
        $this->table[$tableName]=$tableNameAlias;
        $this->tableSql[]=$tableName==$tableNameAlias?"`$tableName`":"`$tableName` `$tableNameAlias`";
        $this->tableField[$tableName]=(array)$field;

        // 是否存在*，存在则直接使用*，不存在则依次插入
        if($field=="*"|| in_array("*",(array)$field)){
            array_unshift($this->fieldSql,"`$tableNameAlias`.*");
        }else{
            foreach($field as $source=>$target){
                $this->fieldSql[]="`$tableNameAlias`.". (is_string($source)? "`$source` as `$target`": "`$target`");
            }
        }
        return $this;
    }
    // 一次添加一个数组，按照 array($table=> $fieldArray) 的格式
    public function addTableArray($tableArray){
        foreach($tableArray as $tableName=>$fieldArray){
            $this->addTable($tableName,$fieldArray);
        }
        return $this;
    }
    // 允许的操作符
    protected static $judgeArray=array(">=","<=","=",">","<","like","<>","!=");
    protected static $opArray=array("and","or");
    // 增加条件值
    public function addCondition($fieldName, $value, $judge="=", $op="and"){
        list($tableName,$fieldName)=$this->formatFieldName($fieldName,".");
        if(isset($this->table[$tableName])){
            $tableName=$this->table[$tableName];
        }
        if(!in_array($tableName,$this->table)){
            if(RUNNING_IN_DEVELOP) var_dump("$fieldName 未加入查询行列");
            return $this;
        }
        // 如果为空 且不是数字 且不是空白保留模式
        if(empty($value)&& !is_numeric($value)&& !$this->ifBlankRemain) return $this;

        $value=is_numeric($value)? 0+$value: $this->db->quote($value);
        $judge=strtolower(trim($judge));
        $judge=in_array($judge,self::$judgeArray)? $judge: "=";
        $op=strtolower(trim($op));
        $op=in_array($op,self::$opArray)? $op: "and";
        $this->conditionSql[]="$op `$tableName`.`$fieldName` $judge $value";
        return $this;
    }
    // 直接传递sql条件
    public function addConditionSql($condition, $op="and"){
        $this->conditionSql[]="$op $condition";
    }
    // 传递id条件
    public function addConditionId($ids,$idName="id"){
        list($tableName,$fieldName)=$this->formatFieldName($idName,".");
        if(isset($this->table[$tableName])){
            $tableName=$this->table[$tableName];
        }
        if(!in_array($tableName,$this->table)){
            if(RUNNING_IN_DEVELOP) var_dump("$fieldName 未加入查询行列");
            return $this;
        }
        $this->addConditionSql($this->db->getIdCondition($ids,"$tableName.$fieldName"));
        return $this;
    }
    // 传递条件数组
    public function addConditionArray($conditionArray){
        foreach($conditionArray as $fieldName=>$value){
            if(asciiEndWith($fieldName,"id")|| asciiEndWith($fieldName,"ids")){
                $this->addConditionId($value,$fieldName);
            }else{
                is_numeric($fieldName)? $this->addConditionSql($value): $this->addCondition($fieldName,$value);
            }
        }
        return $this;
    }
    // 增加表连接关系
    public function addRefer($fieldName,$fieldName2){
        // 验证第一个字段
        list($tableName,$fieldName)=$this->formatFieldName($fieldName);
        if(isset($this->table[$tableName])){
            $tableName=$this->table[$tableName];
        }
        // 验证第二个字段
        list($tableName2,$fieldName2)=$this->formatFieldName($fieldName2);
        if(isset($this->table[$tableName2])){
            $tableName2=$this->table[$tableName2];
        }
        if(!in_array($tableName,$this->table)|| !in_array($tableName2,$this->table)){
            if(RUNNING_IN_DEVELOP) var_dump("在添加关系时，$tableName/$tableName2 未加入查询行列");
            return $this;
        }
        $this->conditionSql[]="and `$tableName`.`$fieldName` = `$tableName2`.`$fieldName2`";
        return $this;
    }
    // array($fieldName=>$fieldName2)
    public function addReferArray($fieldNameArray){
        foreach($fieldNameArray as $fieldName=>$fieldName2){
            $this->addRefer($fieldName,$fieldName2);
        }
        return $this;
    }
    // 增加排序方式，需要的数组格式：
    // array("字段名"=>1|0|true|false|'asc'|'desc') bool值表示是不是采用递减顺序
    public function setSortBy($sortByArray){
        foreach ($sortByArray as $fieldName => $sortType) {
            list($tableName,$fieldName)=$this->formatFieldName($fieldName);
            if(isset($this->table[$tableName])){
                $tableName=$this->table[$tableName];
            }
            if(!in_array($tableName,$this->table)){
                if(RUNNING_IN_DEVELOP) var_dump("$tableName 未加入查询行列");
                return $this;
            }
            if(is_null($sortType)|| $sortType=="") continue;
            $sortType=$sortType==1||$sortType===true||strtolower($sortType)=="desc"? 'DESC': 'ASC';
            $this->sortSql[]="$tableName.$fieldName $sortType";
        }
        return $this;
    }
    public function getSql($rows=null, $offset=0, $attach=''){
        return $this->getCustomSql(implode(",", $this->fieldSql), $rows, $offset, $attach);
    }
    public function getIdSql($rows=null, $offset=0, $attach=''){
        // TODO 有一点漏洞，只能获取单表的
        return $this->getCustomSql("`id`", $rows, $offset, $attach);
    }
    public function getCountSql($rows=null, $offset=0, $attach=''){
        return $this->getCustomSql("count(1)", $rows, $offset, $attach);
    }
    protected function getCustomSql($paramSql, $rows=null, $offset=0, $attach=''){
        $sql="SELECT ";
        $sql.=$this->ifFoundRows?"SQL_CALC_FOUND_ROWS ":"";
        $sql.=$paramSql;
        $sql.=" FROM ".implode(",", $this->tableSql);
        $sql.=" WHERE 1 ".implode(" ", $this->conditionSql);
        if(!empty($this->sortSql)){
            $sql.=" ORDER BY ".implode(",", $this->sortSql);
        }
        $sql.= " ".$attach;
        if(!is_null($rows)){
            $sql.=" LIMIT ".(int)$offset.",".(int)$rows;
        }
        return $sql;
    }
}

/**
 * 数据库结果集，用于foreach循环
 * @author linyh,sg
 */
class SqlDBStatement implements Iterator {
    /**
     * 记录当前是第几条记录
     * @var int
     */
    protected $currentNum=0;
    /** @var array */
    protected $currentData;
    // 要处理的PDOStatement
    /** @var PDOStatement  */
    protected $statement;
    /** @var Callable */
    protected $dealFunction=null;

    /** @param PDOStatement $s */
    public function __construct(PDOStatement $s) {
        $this->statement=$s;
    }

    public function getArray(){
        return iterator_to_array($this,false);
    }

    /**
     * 设置每一行数据的处理方式
     * 参数需要时一个函数，有一个参数，类型为数组，用于处理每一条获取到的数据
     * 注意，最后的时候必须要返回处理完的值
     * @param callable $f
     */
    public function setDealMethod($f){
        if(is_callable($f)){
            $this->dealFunction=$f;
        }
    }

    // Iterator接口函数调用顺序：rewind\next valid current key ……
    public function rewind(){
        $this->currentData=$this->statement->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_FIRST);
        $this->currentNum=0;
    }
    public function next(){
        $this->currentNum++;
        $this->currentData=$this->statement->fetch(PDO::FETCH_ASSOC,PDO::FETCH_ORI_NEXT);
    }
    public function valid(){
        return isset($this->currentData)&&!empty($this->currentData);
    }
    public function current(){
        if($f=$this->dealFunction){
            return $f($this->currentData);
        }else{
            return $this->currentData;
        }
    }
    public function key(){
        return $this->currentNum;
    }
}